package com.gframework.datastructure.trie;

import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;

/**
 * StorageTrieTree缓存包装类.
 * <p>
 * 本类可以包装任意 {@link StorageTrieTree} 接口对象，并提供缓存操作
 * </p>
 * <p>
 * 但是缓存是有条件的。
 * 缓存使用软引用的形式，只有存在此项内容且key的长度在指定范围内时，才会进行缓存。这个参数可以通过构造方法进行设置，默认时16。
 * 即key长度超过16则不会进行缓存，否则会进行缓存。
 * </p>
 * 
 * @since 1.0.0
 * @author Ghwolf
 * @param <T> 存储内容的类型
 * 
 * @see SeparatorTrieTree
 * 
 */
public class CacheStorageTrieTreeWrapper<T> implements StorageTrieTree<T>{
	
	/**
	 * 默认key缓存长度大小
	 */
	private static final int DEFAULT_CACHE_MAX_KEY_LENGTH = 16;
	/**
	 * 参数查询缓存
	 */
	private ConcurrentReferenceHashMap<String, T> cache = new ConcurrentReferenceHashMap<>(32, ReferenceType.SOFT);

	/**
	 * key字符串长度在多长以内允许被缓存
	 */
	private final int cacheMaxKeyLength;
	
	/**
	 * 原始操作对象
	 */
	private StorageTrieTree<T> proxy ;
	
	/**
	 * 创建一个以"."进行分割的，默认缓存key长度的trie树.
	 * @param proxy 被代理对象
	 */
	public CacheStorageTrieTreeWrapper(StorageTrieTree<T> proxy){
		this(proxy,DEFAULT_CACHE_MAX_KEY_LENGTH);
	}
	
	/**
	 * 创建一个以"."进行分割的，指定缓存key长度的trie树
	 * @param proxy 被代理对象
	 * @param cacheMaxKeyLength key长度不超过多少时才缓存。
	 */
	public CacheStorageTrieTreeWrapper(StorageTrieTree<T> proxy,int cacheMaxKeyLength){
		this.cacheMaxKeyLength = cacheMaxKeyLength;
		this.proxy = proxy;
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public T find(String key) {
		boolean canCache = key != null && key.length() <= this.cacheMaxKeyLength;
		if (canCache) {
			T t = this.cache.get(key);
			if (t != null) return t;
		}
		T t = proxy.find(key);
		if (t != null && canCache) {
			this.cache.put(key,t);
		}
		return t;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * 删除一个节点内容的同时会清除该节点key的缓存
	 * </p>
	 */
	@Override
	public T removeValue(String key) {
		T t = proxy.removeValue(key);
		if (t != null) {
			this.cache.remove(key);
		}
		return t;
	}

	/**
	 * {@inheritDoc}
	 * <p>
	 * 需要注意的时，执行此方法会清空所有缓存，而不是从缓存中删除前缀相同的key值，
	 * 在数据量极大的情况下，遍历SoftMap本身会造成很大的性能影响，因此本方法不会采取遍历删除，而是清空缓存。
	 * </p>
	 */
	@Override
	public void remove(String key) {
		proxy.remove(key);
		this.cache.clear();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public T add(String key, T value) {
		T t = proxy.add(key, value);
		this.cache.remove(key);
		return t;
	}

	@Override
	public boolean containsKey(String key) {
		return proxy.containsKey(key);
	}

	@Override
	public String getFormatString(String title) {
		return proxy.getFormatString(title);
	}
	
}
